/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						       basic_dialog.cpp

	$Header: /game/basic_dialog.cpp $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "basic_dialog.h"

#include "adaptor_handler.h"
#include "artifact.h"
#include "artifact_icon.h"
#include "artifact_icon_ptr.h"
#include "bitmap_group_ptr.h"
#include "bitmap_group_window.h"
#include "bitmap_layer_ptr.h"
#include "bitmap_layer_cache_window.h"
#include "bitmap_layer_cache_window_ptr.h"
#include "bound_handler.h"
#include "button.h"
#include "button_cache.h"
#include "creature.h"
#include "creature_array.h"
#include "creature_stack.h"
#include "creature_traits.h"
#include "dialog_box_cache.h"
#include "hero.h"
#include "interface_help.h"
#include "font.h"
#include "format_string.h"
#include "function_adaptors.h"
#include "interface_help.h"
#include "luck_icon_window.h"
#include "material_icons.h"
#include "material_names.h"
#include "morale_icon_window.h"
#include "skill_icon.h"
#include "skill_properties.h"
#include "spell_icon_window.h"
#include "spell_properties.h"
#include "standard_fonts.h"
#include "text_window.h"

extern t_button_cache g_ok_button;
extern t_external_string const k_text_day;

t_dialog_box_cache const k_small_text_background( "text_background.small" );
t_dialog_box_cache const k_large_text_background( "text_background.large" );
extern t_bitmap_group_cache const   k_frame_52( "button.frame_52" );
extern t_bitmap_group_cache const   k_spell_frames( "icons.spell_frames" );

// ---------------------------------------------------------
// create a text background of a given client area
// ---------------------------------------------------------
static t_window_background* create_text_background( t_screen_rect const& rect, t_window* parent )
{
	t_cached_ptr<t_dialog_box_bitmaps> bitmaps;
	t_window_background*			   result;

	if (rect.height() < 60)
		bitmaps = k_small_text_background.get();
	else
		bitmaps = k_large_text_background.get();

	result = new t_window_background( bitmaps, parent );
	result->open_around( rect );
	return result;
}

// ---------------------------------------------------------
// a dialog box with variable contents: text, several display windows, one or more buttons.
// ---------------------------------------------------------
t_basic_dialog::t_basic_dialog( t_window* parent ) : t_dialog_box( parent ), m_color(0,0,0)
{
	m_font = get_font( 18 );
	m_drop_shadow = true;
	m_drop_shadow_color = t_pixel_24(200,200,200);
//	t_window::set_drop_shadow();
}

// ---------------------------------------------------------
// a dialog box with variable contents: text, several display windows, one or more buttons.
// ---------------------------------------------------------
void t_basic_dialog::add_button( t_button* button )
{
	m_buttons.push_back( button );
}

// ---------------------------------------------------------
// a dialog box with variable contents: text, several display windows, one or more buttons.
// ---------------------------------------------------------
void t_basic_dialog::close_click( t_button* button )
{
	close();
}

// ---------------------------------------------------------
// a dialog box with variable contents: text, several display windows, one or more buttons.
// ---------------------------------------------------------
void t_basic_dialog::close_click_result( t_button* button, int result )
{
	m_return_value = result;
	close();
}

// ---------------------------------------------------------
// a dialog box with variable contents: text, several display windows, one or more buttons.
// ---------------------------------------------------------
void t_basic_dialog::add_close_button( t_button* button, int result )
{
	button->set_click_handler( add_2nd_argument( bound_handler( *this, &t_basic_dialog::close_click_result ), result ) );
	m_buttons.push_back( button );
}

// ---------------------------------------------------------
// a dialog box with variable contents: text, several display windows, one or more buttons.
// ---------------------------------------------------------
void t_basic_dialog::add_display_window( t_window* window, std::string const& label )
{
	m_display_windows.push_back( window );
	if (!label.empty())
	{
		t_text_window*  text_window;
		t_string_vector wrapped_text;
		t_screen_rect	rect;

		rect.left = 0;
		rect.top = 0;
		if (label.empty())
		{
			rect.bottom = 0;
			rect.right = 0;
		}
		else
		{
			rect.right = m_font->wrap_text( wrapped_text, label.c_str(), 40, 160 );
			rect.bottom = wrapped_text.size() * m_font->get_height();
		}

		text_window = new t_text_window( m_font, rect, this, label, m_color );
		text_window->set_drop_shadow( m_drop_shadow, m_drop_shadow_color );
		m_labels.push_back( text_window );
	}
}

// ---------------------------------------------------------
// a dialog box with variable contents: text, several display windows, one or more buttons.
// ---------------------------------------------------------
static t_bitmap_group_cache const k_title_bitmap( "dialog.title" );

void t_basic_dialog::create_title()
{
	t_bitmap_layer const* layer;
	t_screen_rect         rect;

	m_title_bitmap = k_title_bitmap.get();
	layer = m_title_bitmap->find( "banner" );
	m_title_background = new t_bitmap_layer_window( layer, t_screen_point(0,0), this );
	rect = m_title_bitmap->find( "text" )->get_rect() - layer->get_rect().top_left();
	m_title = new t_text_window( get_font( rect.height() / 2 ), rect, m_title_background,
		                         "", t_pixel_24(0,0,0) );
	m_title->set_drop_shadow( false );
	m_title->set_center_horizontal();
	m_title->set_center_vertical();
}

// ---------------------------------------------------------
// a dialog box with variable contents: text, several display windows, one or more buttons.
// ---------------------------------------------------------
void t_basic_dialog::set_title( std::string const& text )
{
	if (m_title == 0)
	{
		if (text.empty())
			return;
		create_title();
	}
	m_title->set_text( text );
}

// ---------------------------------------------------------
// compute the area of a window list
// ---------------------------------------------------------
static t_screen_point find_extent(	t_window_list::const_iterator begin,
									t_window_list::const_iterator end )
{
	t_window_list::const_iterator index;
	int                           height;
	int                           width;
	t_screen_point                result(0,0);

	for (index = begin; index != end; index++)
	{
		width = (*index)->get_width();
		height = (*index)->get_height();
		if (width > result.x)
			result.x = width;
		if (height > result.y)
			result.y = height;
	}
	return result;
}

// ---------------------------------------------------------
// a dialog box with variable contents: text, several display windows, one or more buttons.
// ---------------------------------------------------------
static int const            k_text_spacing = 10;
static int const			k_title_spacing = 0;
static t_screen_point const k_display_spacing( 10, 10 );
static t_screen_point const k_label_spacing( 10, 10 );
static t_screen_point const k_button_spacing( 10, 0 );

// ---------------------------------------------------------
// arrange a window list
// ---------------------------------------------------------
int get_size( t_window_list::iterator index, t_window_list::iterator list_end )
{
	int result = 0;

	for (; index != list_end; ++index)
		++result;
	return result;
}

static int arrange( t_window_list::iterator list_begin, t_window_list::iterator list_end, 
					 t_screen_point extent,
				     t_screen_point spacing, t_screen_rect& rect,
					 t_vertical_alignment alignment )
{
	if (list_begin == list_end)
		return 0;

	t_screen_point			size = extent;
	int						list_size = get_size( list_begin, list_end );
	t_window_list::iterator index;
	t_screen_point			point = rect.top_left();
	t_screen_rect			window_rect;
	int						start_x;

	size.x = extent.x * list_size + (list_size - 1) * spacing.x;
	if (rect.right > 0)
		point.x += ( rect.width() - size.x ) / 2;

	if (alignment == k_align_bottom)
		point.y += size.y;

	start_x = point.x;
	for (index = list_begin; index != list_end; index++)
	{
		window_rect = (*index)->get_client_rect();
		window_rect += point;
		// align vertically
		switch (alignment)
		{
			case k_align_center:
				window_rect += t_screen_point( 0, (extent.y - window_rect.height()) / 2 );
				break;

			case k_align_bottom:
				window_rect += t_screen_point( 0, -window_rect.height() );
				break;
		}
		// center horizontally
		window_rect += t_screen_point( ( extent.x - window_rect.width() ) / 2, 0 );
		(*index)->move( window_rect );
		point.x += extent.x + spacing.x;
	}
	rect.top += size.y + spacing.y;
	return window_rect.right - start_x;
}

// ------------------------------------------------------------------------------
// arrange a window list.  Figure out size of each display/label combination.
// ------------------------------------------------------------------------------
static int arrange_with_size( t_window_list::iterator	list_begin, 
							  t_window_list::iterator	list_end,
							  t_screen_point			extent,
							  std::vector<int> const&	size_list,
							  t_screen_point			spacing, 
							  t_screen_rect&			rect,
							  t_vertical_alignment		alignment )
{
	if (list_begin == list_end)
		return 0;

	t_screen_point			size = extent;
	int						list_size = get_size( list_begin, list_end );
	t_window_list::iterator index;
	t_screen_point			point = rect.top_left();
	t_screen_rect			window_rect;
	int                     i;
	int						start_x;

	size.x = 0;
	for ( i=0; i < list_size; i++)
    {
        // Get total size.
        size.x += size_list[i];
    }
	size.x += ((list_size - 1) * spacing.x);

	if (rect.right > 0)
		point.x += ( rect.width() - size.x ) / 2;

	if (alignment == k_align_bottom)
		point.y += size.y;

	start_x = point.x;
    i = 0;
	for (index = list_begin; index != list_end; index++)
	{
		window_rect = (*index)->get_client_rect();
		window_rect += point;
		// align vertically
		switch (alignment)
		{
			case k_align_center:
				window_rect += t_screen_point( 0, (extent.y - window_rect.height()) / 2 );
				break;

			case k_align_bottom:
				window_rect += t_screen_point( 0, -window_rect.height() );
				break;
		}
		// Center horizontally
		window_rect += t_screen_point( ( size_list[i] - window_rect.width() ) / 2, 0 );
		(*index)->move( window_rect );

        point.x += size_list[i] + spacing.x;
        i++;
	}
	rect.top += size.y + spacing.y;
	return window_rect.right - start_x;
}

// ---------------------------------------------------------
// do a single-line arrangement of display windows
// ---------------------------------------------------------
static int arrange_display_window_line( t_window_list::iterator display_windows_begin,
									    t_window_list::iterator display_windows_end, 
										t_window_list::iterator	labels_begin,
										t_window_list::iterator labels_end,
										t_screen_rect&			client_rect,
										bool					non_uniform_size )
{
	t_screen_point extent;
	t_screen_point label_extent;
	int				total_width = 0;
	int				width;

	extent = find_extent( display_windows_begin, display_windows_end );
	label_extent = find_extent( labels_begin, labels_end );

	if (extent.x < label_extent.x)
		extent.x = label_extent.x;
	label_extent.x = extent.x;

    /*
    ** Set non_uniform bool to true if we want to space out m_display_windows and
    ** m_labels individually.  Both display and label lists should be equal.
    */
	if ( non_uniform_size && labels_begin != labels_end )
    {
        std::vector<int>		size_list;
    	t_window_list::iterator index;
    	t_window_list::iterator labels_index;

	    labels_index = labels_begin;
	    for (index = display_windows_begin; index != display_windows_end; index++)
        {
    	    int  size;

            size = (*index)->get_width();
            // Get larger of the two (display or label)
            if (labels_index != labels_end)
            {
                if (size < (*labels_index)->get_width())
                    size = (*labels_index)->get_width();
                labels_index++;
            }

            // Push back the larger of the two.
            size_list.push_back( size );
        }
	    
        total_width = arrange_with_size( display_windows_begin, display_windows_end,
										 extent, size_list, k_display_spacing, 
										 client_rect, k_align_bottom );
		width = arrange_with_size( labels_begin, labels_end, label_extent, size_list,
								   k_label_spacing, client_rect, k_align_top );
    }
    else
    {
	    total_width = arrange( display_windows_begin, display_windows_end, extent, 
			                   k_display_spacing, client_rect, k_align_bottom );
	    width = arrange( labels_begin, labels_end, label_extent, k_label_spacing, client_rect,
						 k_align_top );
    }
	if (total_width < width)
		total_width = width;
	return total_width;
}

// ---------------------------------------------------------
// arrange all display windows, and split into multiple lines if required
// ---------------------------------------------------------
static void arrange_display_windows( t_window_list::iterator display_windows_begin,
								    t_window_list::iterator	display_windows_end,
									t_window_list::iterator	labels_begin,
									t_window_list::iterator labels_end, 
									t_screen_rect&			client_rect,
									bool					non_uniform_size,
									int						width_limit )
{
	t_screen_rect	rect;
	int				width;

	while (true)
	{
		// arrange the windows without limit.
		rect = client_rect;
		width = arrange_display_window_line( display_windows_begin, display_windows_end,
											 labels_begin, labels_end, rect, non_uniform_size );
		if (width <= width_limit)
		{
			client_rect = rect;
			return;
		}
		// multiple lines required.  Ack.
		int						estimated_lines = (width + width_limit - 1) / width_limit;
		int						target_width = width / estimated_lines;
		t_window_list::iterator display_window;
		t_window_list::iterator	label;

		// keep adding windows until we exceed the target width
		display_window = display_windows_begin;
		label = labels_begin;
		while (display_window != display_windows_end)
		{
			++display_window;
			if (label != labels_end)
				++label;
			// determine how wide the line will be, ending with display window / label
			rect = client_rect;
			width = arrange_display_window_line( display_windows_begin, display_window,
												 labels_begin, label, rect, non_uniform_size );
			if (width < target_width)
				continue;
			if (width > width_limit)
			{
				// if we exceeded our overall limit, back up one.
				--display_window;
				// but insure that we always do at least one.
				if (display_window == display_windows_begin)
				{
					++display_window;
					break;
				}
				if (label != labels_begin)
					--label;
				rect = client_rect;
				width = arrange_display_window_line( display_windows_begin, display_window,
													 labels_begin, label, rect, non_uniform_size );
			}
			break;
		}
		// mark the windows 
		client_rect = rect;
		display_windows_begin = display_window;
		labels_begin = label;
	}
}


// ---------------------------------------------------------
// arrange all windows.  Called twice, once to find size, once
// to center objects.
// ---------------------------------------------------------
void t_basic_dialog::arrange_all( t_screen_rect const& rect, bool non_uniform_size,
								  t_screen_rect const& parent_rect )
{
	t_screen_rect window_rect;

	// arrange the items.
	t_screen_rect client_rect( get_left_border(), get_top_border(), 0, 0 );

	if (rect.right > 0)
	{
		client_rect.right = rect.right - get_right_border();
		client_rect.bottom = rect.bottom - get_bottom_border();
	}

	if (m_title_background != 0)
	{
		window_rect = m_title_background->get_client_rect();
		if (rect.right > 0)
			window_rect += t_screen_point( (rect.width() - window_rect.width()) / 2, 0 );
		m_title_background->move( window_rect );
		window_rect.bottom += k_title_spacing;
		if (window_rect.bottom > client_rect.top)
			client_rect.top = window_rect.bottom;
	}

	if (m_text_window != 0)
	{
		window_rect = m_text_background->get_client_rect();
		if (client_rect.right > 0)
			window_rect += t_screen_point( (client_rect.width() - window_rect.width()) / 2
										   + client_rect.left, client_rect.top );
		m_text_background->move( window_rect );
		window_rect = m_text_window->get_client_rect()
			        + t_screen_point( 0, window_rect.top );
		window_rect += t_screen_point(0, m_text_background->get_top_border() );
		if (client_rect.right > 0)
			window_rect += t_screen_point( (client_rect.width() - window_rect.width()) / 2
			                               + client_rect.left, 0 );
		m_text_window->move( window_rect );
		client_rect.top += m_text_background->get_height() + k_text_spacing;
	}

	int width_limit = parent_rect.width() - get_left_border() - get_right_border();
	
	arrange_display_windows( m_display_windows.begin(), m_display_windows.end(),
							 m_labels.begin(), m_labels.end(), client_rect, non_uniform_size,
							 width_limit );
	
	t_screen_point extent;
	t_screen_point label_extent;

	extent = find_extent( m_buttons.begin(), m_buttons.end() );
	arrange( m_buttons.begin(), m_buttons.end(), extent, k_button_spacing, client_rect, 
			 k_align_bottom );
}


// ---------------------------------------------------------
// a dialog box with variable contents: text, several display windows, one or more buttons.
// ---------------------------------------------------------
void t_basic_dialog::open( t_screen_point point, t_screen_rect const& parent_rect,
						   bool center_horizontal, t_vertical_alignment vertical_alignment, 
						   bool non_uniform_size )
{
	t_screen_rect rect(0,0,0,0);

	arrange_all( rect, non_uniform_size, parent_rect );
	set_size_from_children();

	rect = get_client_rect();
	rect.right += get_left_border() + get_right_border();
	rect.bottom += get_top_border() + get_bottom_border();
	arrange_all( rect, non_uniform_size, parent_rect );

	// attempt to center the rectangle, if needed.
	if (center_horizontal)
		point -= t_screen_point( rect.width() / 2, 0 );
	switch (vertical_alignment)
	{
		case k_align_top:
			break;

		case k_align_center:
			point -= t_screen_point( 0, rect.height() / 2 );
			break;

		case k_align_bottom:
			point -= t_screen_point( 0, rect.height() );
			break;
	}
	rect += point;

	// don't let dialog box go off the edge of the parent.
	init( rect );
	fit_to_parent_rect();
}

// ---------------------------------------------------------
// a dialog box with variable contents: text, several display windows, one or more buttons.
// open centered in a window
// ---------------------------------------------------------
void t_basic_dialog::open( bool non_uniform_size )
{
	t_screen_rect  rect = get_parent()->get_client_rect();
	t_screen_point point( rect.width() / 2, rect.height() / 2 );

	open( point, rect, true, k_align_center, non_uniform_size );
}


// ---------------------------------------------------------
// a dialog box with variable contents: text, several display windows, one or more buttons.
// ---------------------------------------------------------
void t_basic_dialog::set_font( t_cached_font font )
{
	m_font = font;
	if (m_text_window != 0)
		m_text_window->set_font( font );
}

// ---------------------------------------------------------
// a dialog box with variable contents: text, several display windows, one or more buttons.
// ---------------------------------------------------------
void t_basic_dialog::set_text( std::string const& text, t_justification justify )
{
    // figure out how much space we need for text.
	if (m_text_window == 0)
	{
		t_string_vector wrapped_text;

		t_screen_rect rect;
		t_window* parent = get_parent();
		int           width = get_main_window()->get_width() - 150;
		int			  height = get_main_window()->get_height() - 200;

		rect.left = 0;
		rect.top = 0;
		rect.right = m_font->wrap_text( wrapped_text, text.c_str(), 0, width );
		rect.bottom = wrapped_text.size() * m_font->get_height();
		if( rect.bottom > height )
			rect.bottom = height;

		m_text_background = create_text_background( rect, this );
		m_text_window = new t_text_window( m_font, rect, this, text.c_str(), m_color );
		m_text_window->set_drop_shadow( m_drop_shadow, m_drop_shadow_color );
		m_text_window->set_justification( justify );
		m_text_window->set_center_vertical();
	}
	else
		m_text_window->set_text( text.c_str() );
}

// ---------------------------------------------------------
// a dialog box with variable contents: text, several display windows, one or more buttons.
// ---------------------------------------------------------
void t_basic_dialog::set_color( t_pixel_24 color )
{
	m_color = color;
	if (m_text_window != 0)
		m_text_window->set_color( m_color );
}

// ---------------------------------------------------------
// a dialog box with variable contents: text, several display windows, one or more buttons.
// ---------------------------------------------------------
void t_basic_dialog::set_drop_shadow( bool arg, t_pixel_24 color )
{
	m_drop_shadow = arg;
	m_drop_shadow_color = color;
	if (m_text_window != 0)
		m_text_window->set_drop_shadow( arg, color );
}

// ---------------------------------------------------------
// a dialog box with variable contents: text, several display windows, one or more buttons.
// ---------------------------------------------------------
void t_basic_dialog::add_ok_button()
{
	t_help_block const&     shared_help = get_help_block( "shared" );
	t_button* ok_button = new t_button( g_ok_button.get(), t_screen_point(0,0),
		                                this );
    set_help( ok_button, shared_help, "ok" );

	add_close_button( ok_button, 1 );
}

// ---------------------------------------------------------
// a dialog box with variable contents: text, several display windows, one or more buttons.
// ---------------------------------------------------------
extern t_button_cache g_cancel_button;

void t_basic_dialog::add_cancel_button()
{
	t_help_block const&     shared_help = get_help_block( "shared" );
	t_button* ok_button = new t_button( g_cancel_button.get(), t_screen_point(0,0),
		                                this );
    set_help( ok_button, shared_help, "cancel" );

	add_close_button( ok_button, 0 );
}


// ---------------------------------------------------------
// a dialog box with variable contents: text, several display windows, one or more buttons.
// add an icon for a material
// ---------------------------------------------------------
void t_basic_dialog::add_material( t_material material, int amount, bool show_plus_sign, bool show_day_text )
{
	t_bitmap_layer_ptr icon = get_material_icon( material );
	t_window*          window;
	std::string        text;

	window = new t_bitmap_layer_cache_window( icon, t_screen_point(0,0), this );
	set_help( window, get_help_block( "material_display" ), k_material_keyword[material] );
    if (show_plus_sign)
	    text = format_string( "%+i %s", amount, k_material_name[material].c_str() );
    else
	    text = format_string( "%i %s", amount, k_material_name[material].c_str() );
    if (show_day_text)
        text = text + format_string("/%s", k_text_day.c_str()).c_str();
	add_display_window( window, text );
}

// ---------------------------------------------------------
// a dialog box with variable contents: text, several display windows, one or more buttons.
// add an icons materials
// ---------------------------------------------------------
void t_basic_dialog::add_materials( t_material_array const& materials, bool show_plus_sign )
{
	int i;

	for (i = 0; i < k_material_count; i++)
		if (materials[i] != 0)
			add_material( t_material(i), materials[i], show_plus_sign );
}


// ---------------------------------------------------------
// a dialog box with variable contents: text, several display windows, one or more buttons.
// add an icon for a experience
// ---------------------------------------------------------
t_external_string    k_text_experience( "experience.dialog" );
t_bitmap_group_cache k_stat_icons_82( "icons.stats.82" );

void t_basic_dialog::add_experience( int amount )
{
	add_statistic( "experience", amount, k_text_experience );
}

t_bitmap_group_cache k_stat_icons_44( "icons.stats.44" );
extern t_bitmap_group_cache g_creature_rings;

void t_basic_dialog::add_experience( int amount, t_hero *hero )
{
	std::string						text;
	t_bitmap_layer_ptr				icon;
	t_bitmap_layer_cache_window_ptr icon_window;
	t_window*						parent;
	t_bitmap_group_ptr			    rings;
	t_bitmap_layer const*			ring;
	t_screen_point					portrait_offset;
	t_pixel_24                      text_color(0,0,0);
	t_cached_font					font = get_font( 18 );
	t_screen_rect					rect(0,0,0,0);
	t_screen_rect					text_rect;

	// create invisible parent to house 2 icons and a text window
	parent = new t_window( rect, k_completely_transparent, this );

	// add the portrait window
	icon = hero->get_portrait(52);
	icon_window = new t_bitmap_layer_cache_window( icon, t_screen_point(0,0), parent, false );

	// grab the ring asset from the bitmap caches
	rings = g_creature_rings.get();
	ring  = rings->find( "drag_border" );

	// add the ring around the creature portrait
	// calculate the relative offset for the ring based on the invisible window container
	portrait_offset = rings->find( "portrait" )->get_rect().top_left();

	t_bitmap_group_window* group_window;

	group_window = new t_bitmap_group_window( rings, -portrait_offset, "drag_border", parent );
	group_window->set_size_from_frame();

	// create and add in the experience icon
	t_bitmap_group_ptr	bitmaps;
	t_screen_point		experience_offset;
	
	bitmaps = k_stat_icons_44.get();
	rect = bitmaps->find( "experience" )->get_rect();
	experience_offset = -rect.top_left();
	experience_offset.x += group_window->get_screen_rect().left;
	experience_offset.x += (ring->get_width() - rect.width()) / 2;
	experience_offset.y += ring->get_height() + 5;
	group_window = new t_bitmap_group_window( bitmaps, experience_offset, "experience", 
	 									      parent );
	group_window->set_size_from_frame();
	parent->set_size_from_children();

	text = format_string("%+d", amount);

	/*
	t_string_vector wrapped_text;
	rect.left = 0;
	rect.top = 0;
	rect.right = m_font->wrap_text( wrapped_text, text.c_str(), 40, 160 );
	rect.bottom = wrapped_text.size() * m_font->get_height();

	t_window_background* background_window = create_text_background(rect, parent);
	t_text_window* window = new t_text_window( m_font, rect, background_window, text.c_str(), m_color );
	window->set_drop_shadow( m_drop_shadow, m_drop_shadow_color );
	window->set_center_horizontal();
	window->set_center_vertical();
	parent->set_size_from_children();
	add_display_window( parent, "" );
	*/

	add_display_window( parent, text );
}

void t_basic_dialog::add_experience_icon()
{
	t_bitmap_group_window* window;
	t_bitmap_group_ptr     bitmaps;
	std::string            text;

	bitmaps = k_stat_icons_82.get();
	window = new t_bitmap_group_window( bitmaps, t_screen_point(0,0), "experience", this );
	window->set_size_from_frame();
	add_display_window( window, "" );
}

extern t_external_string const k_text_speed( "speed.dialog" );

void t_basic_dialog::add_speed( int amount )
{
	add_statistic( "speed", amount, k_text_speed );
}

extern t_external_string const k_text_movement;

void t_basic_dialog::add_movement( int amount )
{
	add_statistic( "movement", amount, k_text_movement );
}

extern t_external_string const k_text_damage( "damage.dialog" );

void t_basic_dialog::add_damage( int low_amount, int high_amount, bool percent_sign )
{
	t_bitmap_group_window* window;
	t_bitmap_group_ptr     bitmaps;
	std::string            text;

	bitmaps = k_stat_icons_82.get();
	window = new t_bitmap_group_window( bitmaps, t_screen_point(0,0), "damage", this );
	window->set_size_from_frame();
    if (percent_sign)
	{
		if ( low_amount == high_amount )
		{
			// "+20% : +20% damage" looks funny
			text = format_string( "%+i%% %s", low_amount, k_text_damage.c_str() );
		}
		else
		{
	    	text = format_string( "%+i%% : %+i%% %s", low_amount, high_amount, k_text_damage.c_str() );
		}
	}
    else
	{
    	text = format_string( "%+i : %+i %s", low_amount, high_amount, k_text_damage.c_str() );
	}

	add_display_window( window, text );
}

void t_basic_dialog::add_statistic( std::string const& keyword, int amount, 
								    std::string const& name, bool percent_sign )
{
	t_bitmap_group_window* window;
	t_bitmap_group_ptr     bitmaps;
	std::string            text;

	bitmaps = k_stat_icons_82.get();
	window = new t_bitmap_group_window( bitmaps, t_screen_point(0,0), keyword, this );
	window->set_size_from_frame();
    if (percent_sign)
    	text = format_string( "%+i%%", amount );
    else
    	text = format_string( "%+i", amount );
	if (!name.empty())
	{
		text += ' ';
		text += name;
	}
	add_display_window( window, text );
}

static t_external_string const k_text_attack( "attack.misc" );

void t_basic_dialog::add_attack( int amount )
{
	add_statistic( "melee_attack", amount, k_text_attack );
}

static t_external_string const k_text_defense( "defense.misc" );

void t_basic_dialog::add_defense( int amount )
{
	add_statistic( "melee_defense", amount, k_text_defense );
}

extern t_external_string const k_text_spell_points( "spell_points.dialog" );

void t_basic_dialog::add_spell_points( int amount )
{
	add_statistic( "spell_points", amount, k_text_spell_points );
}

extern t_external_string const k_text_hit_points( "hit_points.dialog" );

void t_basic_dialog::add_hit_points( int amount, bool percent_sign )
{
	add_statistic( "hit_points", amount, k_text_hit_points, percent_sign );
}


// ---------------------------------------------------------
// a dialog box with variable contents: text, several display windows, one or more buttons.
// add an icon for a spell points
// ---------------------------------------------------------
void t_basic_dialog::add_mana( int amount )
{
	add_statistic( "spell_points", amount, k_text_spell_points );
}

// ---------------------------------------------------------
// a dialog box with variable contents: text, several display windows, one or more buttons.
// add an icon for an artifact
// ---------------------------------------------------------
void t_basic_dialog::add_artifact( t_artifact const& artifact, int amount, bool frame )
{
	t_artifact_icon_ptr artifact_icon;
	std::string        text;
	
    if (amount == 1)
	    text = artifact.get_name();
    else
	    text = format_string( "%s (%i)", artifact.get_name().c_str(), amount );

    if (!frame)
    {
    	artifact_icon = new t_artifact_icon( artifact, t_screen_point(0,0), this );
    	add_display_window( artifact_icon, text );
        return;
    }

    t_bitmap_group_ptr      frames_bitmaps = k_frame_52.get();
	t_window*		        window;
	t_bitmap_layer const*	layer;
	t_screen_rect           rect(0,0,52,52);

    // Holder
    t_window* window_holder = new t_window( rect, t_window::k_completely_transparent, this );
    t_screen_rect icon_rect = frames_bitmaps->find( "icon" )->get_rect();
    
    // Icon
    artifact_icon = new t_artifact_icon( artifact, icon_rect.top_left(), window_holder );
    int artifact_width = artifact_icon->get_width();
    int artifact_height = artifact_icon->get_height();
	
    // Frame.
    layer = frames_bitmaps->find( "released" );
	window = new t_bitmap_layer_window( layer, t_screen_point(0,0), window_holder );

    window_holder->set_size_from_children();

    t_screen_point artifact_point = t_screen_point( (icon_rect.width() - artifact_width)/2,
                    (icon_rect.height() - artifact_height)/2 );
    artifact_point += icon_rect.top_left();
    artifact_icon->move( artifact_point );
    
	add_display_window( window_holder, text );
}

// ---------------------------------------------------------
// a dialog box with variable contents: text, several display windows, one or more buttons.
// add an icon for a skill
// ---------------------------------------------------------
void t_basic_dialog::add_skill( t_skill const& skill, bool frame )
{
	t_screen_rect       rect(0,0,52,52);

    if (!frame)
    {
	    t_skill_icon*       icon = new t_skill_icon( skill, rect, this );
	    add_display_window( icon, get_name( skill ));
        return;
    }

	t_bitmap_group_ptr      frames_bitmaps = k_frame_52.get();
	t_window*		        window;
	t_bitmap_layer const*	layer;

    // Holder
    t_window* window_holder = new t_window( rect, t_window::k_completely_transparent, this );
    t_screen_point icon_point = frames_bitmaps->find( "icon" )->get_rect().top_left();
    
    // Icon
	t_skill_icon* icon = new t_skill_icon( skill, rect + icon_point, window_holder );
	
    // Frame.
    layer = frames_bitmaps->find( "released" );
	window = new t_bitmap_layer_window( layer, t_screen_point(0,0), window_holder );
	
    window_holder->set_size_from_children();

	add_display_window( window_holder, get_name( skill ));
}

// ---------------------------------------------------------
// a dialog box with variable contents: text, several display windows, one or more buttons.
// add an icon for a creature
// ---------------------------------------------------------
extern t_bitmap_group_cache g_creature_rings;

// ---------------------------------------------------------
// add a single creature portait with some externally determined
// caption below it
// ---------------------------------------------------------
void t_basic_dialog::add_creature_icon( t_creature_type type, const std::string &label, const std::string &help)
{
	t_creature creature( type, 1 );

	add_creature_icon(creature, label, help);
}

// ---------------------------------------------------------
// add a single creature portait with some externally determined
// caption below it
// ---------------------------------------------------------
void t_basic_dialog::add_creature_icon( t_creature_stack const& creature, const std::string &label, const std::string &help)
{
	t_bitmap_group_ptr rings = g_creature_rings.get();
	t_window*          container;
	t_window*		   window;
	t_screen_point     offset;

	container = new t_window( t_screen_rect(0,0,0,0), k_completely_transparent, this );
	offset = rings->find( "portrait" )->get_rect().top_left();
	window = new t_bitmap_layer_cache_window( creature.get_portrait(), offset, container, false );
	
	if (!help.empty())
		window->set_help_balloon_text( help );

	t_bitmap_group_window* frame_window;
	
	frame_window = new t_bitmap_group_window( rings, t_screen_point(0,0), "drag_border",
		                                container );
	frame_window->set_size_from_frame();
	container->set_size_from_children();
	add_display_window( container, label );
}

// ---------------------------------------------------------
// add a single creature
// ---------------------------------------------------------
void t_basic_dialog::add_creature( t_creature_stack const& creature, bool approximate )
{
	std::string        help;
	std::string        label;

	help = creature.get_name();
	label = help;
	if (creature.get_creature() != 0)
	{
		if (approximate)
		{
			help = get_army_size_name( creature.get_number(), k_article_a ) + " " 
				     + creature.get_name(false, 2);
			label = get_army_size_name( creature.get_number(), k_article_none );
			help[0] = toupper( help[0] );
			label[0] = toupper( label[0] );
		}
		else
		{
			label = format_string( "%i", creature.get_number() );
			help = label + " " + help;
		}
	}

	add_creature_icon(creature, label, help);
}

// ---------------------------------------------------------
// add a single creature
// ---------------------------------------------------------
void t_basic_dialog::add_creature( t_creature_type type, int amount, bool approximate )
{
	t_creature creature( type, amount );

	add_creature( creature, approximate );
}

// ---------------------------------------------------------
// add a group of creatures
// ---------------------------------------------------------
void t_basic_dialog::add_creatures( t_creature_array const& creatures, bool approximate )
{
	int i;

	for (i = 0; i < t_creature_array::k_size; i++)
		if (creatures[i].get_number() > 0)
			add_creature( creatures[i], approximate );
}

// ---------------------------------------------------------
// add a spell
// ---------------------------------------------------------
void t_basic_dialog::add_spell( t_spell spell, bool frame )
{
    t_spell_icon_window* spell_window;

    if (!frame)
    {
	    spell_window = new t_spell_icon_window( spell, t_screen_rect(0,0,52,52), this );
    	add_display_window( spell_window, get_spell_name( spell ));
        return;
    }
	
    t_bitmap_group_ptr      frames_bitmaps = k_frame_52.get(); // k_spell_frames.get();
	t_window*		        window;
	t_bitmap_layer const*	layer;
	t_screen_rect           rect(0,0,52,52);

    // Holder
    t_window* window_holder = new t_window( rect, t_window::k_completely_transparent, this );
    t_screen_point icon_point = frames_bitmaps->find( "icon" )->get_rect().top_left();
    
    // Icon
    spell_window = new t_spell_icon_window( spell, rect + icon_point, window_holder );
	
    // Frame.
    layer = frames_bitmaps->find( "released" );
	window = new t_bitmap_layer_window( layer, t_screen_point(0,0), window_holder );
	
    window_holder->set_size_from_children();

	add_display_window( window_holder, get_spell_name( spell ));
}

// ---------------------------------------------------------
// add a luck icon
// ---------------------------------------------------------
void t_basic_dialog::add_luck( int amount )
{
	t_window*     window;
	t_screen_rect rect(0,0,52,52);

	window = new t_luck_icon_display( rect, this, amount );
	add_display_window( window, format_string( "%+i", amount ) );
}

// ---------------------------------------------------------
// add a morale icon
// ---------------------------------------------------------
void t_basic_dialog::add_morale( int amount )
{
	t_window*     window;
	t_screen_rect rect(0,0,52,52);

	window = new t_morale_icon_display( rect, this, amount );
	add_display_window( window, format_string( "%+i", amount ) );
}

